package com.imgrate.migration.service;

import com.imgrate.migration.entity.MigrationTask;
import com.imgrate.migration.entity.Server;
import com.imgrate.migration.repository.MigrationTaskRepository;
import com.imgrate.migration.repository.ServerRepository;
import com.imgrate.migration.service.SshService.CommandResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.ArrayList;
import java.util.Arrays;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
@RequiredArgsConstructor
@Slf4j
public class MigrationService {
    
    private final MigrationTaskRepository migrationTaskRepository;
    private final ServerRepository serverRepository;
    private final SshService sshService;
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    // 项目根目录路径
    private static final String PROJECT_ROOT = System.getProperty("user.dir");
    // rclone安装脚本路径
    private static final String RCLONE_SCRIPT_LINUX = PROJECT_ROOT + "/scripts/install-rclone-detailed.sh";
    private static final String RCLONE_SCRIPT_WINDOWS = PROJECT_ROOT + "/scripts/install-rclone-windows-local.ps1";
    
    // rclone安装包路径
    private static final String RCLONE_LINUX_PACKAGE = PROJECT_ROOT + "/scripts/rclone-current-linux-amd64.zip";
    private static final String RCLONE_WINDOWS_PACKAGE = PROJECT_ROOT + "/scripts/rclone-current-windows-amd64.zip";
    
    public MigrationTask createMigrationTask(MigrationTask task) {
        task.setStatus("PENDING");
        task.setProgress(0);
        task.setCreatedAt(LocalDateTime.now());
        task.setUpdatedAt(LocalDateTime.now());
        MigrationTask savedTask = migrationTaskRepository.save(task);
        log.info("[任务创建] 创建迁移任务: {} (ID: {})", task.getTaskName(), savedTask.getId());
        return savedTask;
    }
    
    public List<MigrationTask> getAllTasks() {
        return migrationTaskRepository.findAll();
    }
    
    public List<MigrationTask> getTasksByStatus(String status) {
        return migrationTaskRepository.findByStatus(status);
    }
    
    public Optional<MigrationTask> getTaskById(Long id) {
        return migrationTaskRepository.findById(id);
    }
    
    public MigrationTask updateTaskStatus(Long id, String status, Integer progress, String errorMessage) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(id);
        if (taskOpt.isPresent()) {
            MigrationTask task = taskOpt.get();
            task.setStatus(status);
            if (progress != null) {
                task.setProgress(progress);
            }
            if (errorMessage != null) {
                task.setErrorMessage(errorMessage);
            }
            if ("RUNNING".equals(status) && task.getStartTime() == null) {
                task.setStartTime(LocalDateTime.now());
            }
            if (("COMPLETED".equals(status) || "FAILED".equals(status)) && task.getEndTime() == null) {
                task.setEndTime(LocalDateTime.now());
            }
            
            MigrationTask updatedTask = migrationTaskRepository.save(task);
            log.info("[状态更新] 更新迁移任务状态: {} (ID: {}) - 状态: {}, 进度: {}%", 
                    task.getTaskName(), id, status, progress);
            return updatedTask;
        }
        log.error("[任务错误] 迁移任务不存在: ID {}", id);
        throw new RuntimeException("迁移任务不存在");
    }
    
    public MigrationTask resetTask(Long id) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(id);
        if (taskOpt.isPresent()) {
            MigrationTask task = taskOpt.get();
            task.setStatus("PENDING");
            task.setProgress(0);
            task.setStartTime(null);
            task.setEndTime(null);
            task.setErrorMessage(null);
            task.setUpdatedAt(LocalDateTime.now());
            
            MigrationTask resetTask = migrationTaskRepository.save(task);
            log.info("[任务重置] 重置迁移任务: {} (ID: {})", task.getTaskName(), id);
            return resetTask;
        }
        log.error("[任务错误] 迁移任务不存在: ID {}", id);
        throw new RuntimeException("迁移任务不存在");
    }
    
    /**
     * 暂停迁移任务
     * @param id 任务ID
     * @return 更新后的任务
     */
    public MigrationTask pauseTask(Long id) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(id);
        if (taskOpt.isPresent()) {
            MigrationTask task = taskOpt.get();
            // 只有运行中的任务才能被暂停
            if ("RUNNING".equals(task.getStatus())) {
                task.setStatus("PAUSED");
                task.setUpdatedAt(LocalDateTime.now());
                
                MigrationTask pausedTask = migrationTaskRepository.save(task);
                log.info("[任务暂停] 暂停迁移任务: {} (ID: {})", task.getTaskName(), id);
                return pausedTask;
            } else {
                log.warn("[状态错误] 只有运行中的任务才能被暂停: {} (ID: {}), 当前状态: {}", task.getTaskName(), id, task.getStatus());
                throw new RuntimeException("只有运行中的任务才能被暂停");
            }
        }
        log.error("[任务错误] 迁移任务不存在: ID {}", id);
        throw new RuntimeException("迁移任务不存在");
    }
    
    /**
     * 恢复迁移任务
     * @param id 任务ID
     * @return 更新后的任务
     */
    public MigrationTask resumeTask(Long id) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(id);
        if (taskOpt.isPresent()) {
            MigrationTask task = taskOpt.get();
            // 只有暂停的任务才能被恢复
            if ("PAUSED".equals(task.getStatus())) {
                task.setStatus("RUNNING");
                task.setUpdatedAt(LocalDateTime.now());
                
                MigrationTask resumedTask = migrationTaskRepository.save(task);
                log.info("[任务恢复] 恢复迁移任务: {} (ID: {})", task.getTaskName(), id);
                return resumedTask;
            } else {
                log.warn("[状态错误] 只有暂停的任务才能被恢复: {} (ID: {}), 当前状态: {}", task.getTaskName(), id, task.getStatus());
                throw new RuntimeException("只有暂停的任务才能被恢复");
            }
        }
        log.error("[任务错误] 迁移任务不存在: ID {}", id);
        throw new RuntimeException("迁移任务不存在");
    }
    
    /**
     * 停止迁移任务
     * @param id 任务ID
     * @return 更新后的任务
     */
    public MigrationTask stopTask(Long id) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(id);
        if (taskOpt.isPresent()) {
            MigrationTask task = taskOpt.get();
            // 只有运行中或暂停的任务才能被停止
            if ("RUNNING".equals(task.getStatus()) || "PAUSED".equals(task.getStatus())) {
                task.setStatus("STOPPED");
                task.setEndTime(LocalDateTime.now());
                task.setUpdatedAt(LocalDateTime.now());
                
                MigrationTask stoppedTask = migrationTaskRepository.save(task);
                log.info("[任务停止] 停止迁移任务: {} (ID: {})", task.getTaskName(), id);
                return stoppedTask;
            } else {
                log.warn("[状态错误] 只有运行中或暂停的任务才能被停止: {} (ID: {}), 当前状态: {}", task.getTaskName(), id, task.getStatus());
                throw new RuntimeException("只有运行中或暂停的任务才能被停止");
            }
        }
        log.error("[任务错误] 迁移任务不存在: ID {}", id);
        throw new RuntimeException("迁移任务不存在");
    }
    
    public void deleteTask(Long id) {
        migrationTaskRepository.deleteById(id);
        log.info("[任务删除] 删除迁移任务ID: {}", id);
    }
    
    /**
     * 执行迁移任务的具体实现
     * @param taskId 任务ID
     */
    public void executeMigration(Long taskId) {
        Optional<MigrationTask> taskOpt = migrationTaskRepository.findById(taskId);
        if (!taskOpt.isPresent()) {
            log.error("[任务错误] 迁移任务不存在: ID {}", taskId);
            throw new RuntimeException("迁移任务不存在");
        }
        
        MigrationTask task = taskOpt.get();
        if (!"PENDING".equals(task.getStatus())) {
            log.warn("[状态错误] 任务状态不正确，无法执行: {} (ID: {}), 当前状态: {}", task.getTaskName(), taskId, task.getStatus());
            throw new RuntimeException("任务状态不正确，无法执行");
        }
        
        log.info("[迁移开始] 开始执行迁移任务: {} (ID: {})", task.getTaskName(), taskId);
        
        // 更新任务状态为运行中
        updateTaskStatus(taskId, "RUNNING", 0, null);
        
        try {
            // 获取源服务器和目标服务器信息
            Optional<Server> sourceServerOpt = serverRepository.findById(task.getSourceServerId());
            Optional<Server> targetServerOpt = serverRepository.findById(task.getTargetServerId());
            
            if (!sourceServerOpt.isPresent() || !targetServerOpt.isPresent()) {
                String errorMsg = "源服务器或目标服务器不存在";
                log.error("[服务器错误] {} - 源服务器ID: {}, 目标服务器ID: {}", errorMsg, task.getSourceServerId(), task.getTargetServerId());
                throw new RuntimeException(errorMsg);
            }
            
            Server sourceServer = sourceServerOpt.get();
            Server targetServer = targetServerOpt.get();
            
            log.info("[服务器信息] 迁移任务 {} (ID: {}) 配置信息 - 源服务器: {}:{} (ID: {}), 目标服务器: {}:{} (ID: {})", 
                    task.getTaskName(), taskId,
                    sourceServer.getHostname(), sourceServer.getPort(), sourceServer.getId(),
                    targetServer.getHostname(), targetServer.getPort(), targetServer.getId());
            
            // 执行具体的迁移步骤
            performMigrationSteps(task, sourceServer, targetServer);
            
            // 更新任务状态为完成
            updateTaskStatus(taskId, "COMPLETED", 100, null);
            log.info("[迁移完成] 迁移任务执行完成: {} (ID: {})", task.getTaskName(), taskId);
            
        } catch (Exception e) {
            log.error("[迁移失败] 迁移任务执行失败: {} (ID: {}) - 错误信息: {}", task.getTaskName(), taskId, e.getMessage(), e);
            updateTaskStatus(taskId, "FAILED", task.getProgress(), e.getMessage());
            throw new RuntimeException("迁移任务执行失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行具体的迁移步骤
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performMigrationSteps(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[步骤开始] 开始执行迁移步骤: {} (ID: {})", task.getTaskName(), task.getId());
            
            // 步骤1: 连接到源服务器 (5%)
            updateTaskStatus(task.getId(), "RUNNING", 5, null);
            log.info("[步骤1] 连接到源服务器 {}:{}", sourceServer.getHostname(), sourceServer.getPort());
            
            // 测试源服务器连接
            SshService.ConnectionResult sourceConnectionResult = sshService.testConnection(sourceServer);
            if (!sourceConnectionResult.isSuccess()) {
                String errorMsg = "源服务器连接失败: " + sourceConnectionResult.getMessage();
                log.error("[连接失败] {} - 服务器: {}:{}", errorMsg, sourceServer.getHostname(), sourceServer.getPort());
                throw new RuntimeException(errorMsg);
            }
            
            // 步骤2: 连接到目标服务器 (10%)
            updateTaskStatus(task.getId(), "RUNNING", 10, null);
            log.info("[步骤2] 连接到目标服务器 {}:{}", targetServer.getHostname(), targetServer.getPort());
            
            // 测试目标服务器连接
            SshService.ConnectionResult targetConnectionResult = sshService.testConnection(targetServer);
            if (!targetConnectionResult.isSuccess()) {
                String errorMsg = "目标服务器连接失败: " + targetConnectionResult.getMessage();
                log.error("[连接失败] {} - 服务器: {}:{}", errorMsg, targetServer.getHostname(), targetServer.getPort());
                throw new RuntimeException(errorMsg);
            }
            
            // 步骤3: 验证源服务器环境 (15%)
            updateTaskStatus(task.getId(), "RUNNING", 15, null);
            log.info("[步骤3] 验证源服务器环境");
            validateServerEnvironment(sourceServer);
            log.info("[步骤3完成] 源服务器环境验证通过");
            
            // 步骤4: 验证目标服务器环境 (20%)
            updateTaskStatus(task.getId(), "RUNNING", 20, null);
            log.info("[步骤4] 验证目标服务器环境");
            validateServerEnvironment(targetServer);
            log.info("[步骤4完成] 目标服务器环境验证通过");
            
            // 步骤5: 准备迁移数据 (25%)
            updateTaskStatus(task.getId(), "RUNNING", 25, null);
            log.info("[步骤5] 准备迁移数据");
            
            if ("FULL_SYSTEM".equals(task.getMigrationType())) {
                // 全系统迁移步骤
                log.info("[全系统迁移] 开始执行全系统迁移");
                performFullSystemMigration(task, sourceServer, targetServer);
                log.info("[全系统迁移完成] 全系统迁移执行完成");
            } else {
                // 部分目录迁移步骤
                log.info("[部分迁移] 开始执行部分目录迁移");
                performPartialMigration(task, sourceServer, targetServer);
                log.info("[部分迁移完成] 部分目录迁移执行完成");
            }
            
            // 步骤6: 验证迁移结果 (90%)
            updateTaskStatus(task.getId(), "RUNNING", 90, null);
            log.info("[步骤6] 验证迁移结果");
            // 添加实际的迁移结果验证逻辑
            verifyMigrationResult(task, sourceServer, targetServer);
            
            // 步骤7: 清理临时文件 (95%)
            updateTaskStatus(task.getId(), "RUNNING", 95, null);
            log.info("[步骤7] 清理临时文件");
            // 添加实际的清理逻辑
            cleanupTemporaryFiles(task, sourceServer, targetServer);
            
        } catch (Exception e) {
            log.error("[步骤失败] 迁移步骤执行失败: {} (ID: {}) - 错误信息: {}", task.getTaskName(), task.getId(), e.getMessage(), e);
            // 重新抛出异常，让外层捕获并正确处理任务状态
            throw new RuntimeException("迁移步骤执行失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 验证服务器环境
     * @param server 服务器
     */
    private void validateServerEnvironment(Server server) {
        log.info("[环境验证] 开始验证服务器环境: {}:{} (ID: {})", server.getHostname(), server.getPort(), server.getId());
        
        // 检查服务器连接
        SshService.ConnectionResult result = sshService.testConnection(server);
        if (!result.isSuccess()) {
            String errorMsg = "服务器连接失败: " + result.getMessage();
            log.error("[连接失败] {} - 服务器: {}:{}", errorMsg, server.getHostname(), server.getPort());
            throw new RuntimeException(errorMsg);
        }
        
        // 根据操作系统类型进行不同的环境验证
        if ("LINUX".equalsIgnoreCase(server.getOsType())) {
            log.info("[Linux验证] 验证Linux环境: {}:{}", server.getHostname(), server.getPort());
            validateLinuxEnvironment(server);
        } else if ("WINDOWS".equalsIgnoreCase(server.getOsType())) {
            log.info("[Windows验证] 验证Windows环境: {}:{}", server.getHostname(), server.getPort());
            validateWindowsEnvironment(server);
        } else {
            log.warn("[未知系统] 未知操作系统类型: {} - 服务器: {}:{}", server.getOsType(), server.getHostname(), server.getPort());
        }
    }
    
    /**
     * 验证Linux环境
     * @param server 服务器
     */
    private void validateLinuxEnvironment(Server server) {
        log.info("[Linux环境] 验证Linux环境: {}", server.getHostname());
        
        // 检查必要的命令是否存在
        String[] requiredCommands = {"rsync", "tar", "scp", "dd"};
        for (String command : requiredCommands) {
            try {
                String checkCommand = "which " + command;
                SshService.CommandResult result = sshService.executeCommand(server, checkCommand);
                if (!result.isSuccess()) {
                    log.warn("[命令缺失] Linux服务器 {} 上未找到命令: {}", server.getHostname(), command);
                } else {
                    log.info("[命令存在] Linux服务器 {} 上找到命令: {}", server.getHostname(), command);
                }
            } catch (Exception e) {
                log.warn("[检查错误] 检查Linux服务器 {} 上的命令 {} 时出错: {}", server.getHostname(), command, e.getMessage());
            }
        }
        
        // 检查并安装rclone
        checkAndInstallRclone(server);
        
        // 检查磁盘空间
        try {
            String diskSpaceCommand = "df -h";
            SshService.CommandResult result = sshService.executeCommand(server, diskSpaceCommand);
            if (result.isSuccess()) {
                log.info("[磁盘空间] Linux服务器 {} 磁盘空间信息:\n{}", server.getHostname(), result.getOutput());
            } else {
                log.warn("[磁盘错误] 无法获取Linux服务器 {} 的磁盘空间信息: {}", server.getHostname(), result.getMessage());
                // 打印错误输出（如果有的话）
                if (result.getMessage() != null && !result.getMessage().isEmpty()) {
                    log.warn("[磁盘错误详情] Linux服务器 {} 磁盘空间检查错误输出: {}", server.getHostname(), result.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("[磁盘检查错误] 检查Linux服务器 {} 的磁盘空间时出错: {}", server.getHostname(), e.getMessage());
        }
    }
    
    /**
     * 验证Windows环境
     * @param server 服务器
     */
    private void validateWindowsEnvironment(Server server) {
        log.info("[Windows环境] 验证Windows环境: {}", server.getHostname());
        
        // 检查PowerShell版本
        try {
            String psVersionCommand = "powershell -Command \"$PSVersionTable.PSVersion\"";
            SshService.CommandResult result = sshService.executeCommand(server, psVersionCommand);
            if (result.isSuccess()) {
                log.info("[PS版本] Windows服务器 {} PowerShell版本: {}", server.getHostname(), result.getOutput());
            } else {
                log.warn("[PS错误] 无法获取Windows服务器 {} 的PowerShell版本: {}", server.getHostname(), result.getMessage());
                // 打印错误输出（如果有的话）
                if (result.getMessage() != null && !result.getMessage().isEmpty()) {
                    log.warn("[PS错误详情] Windows服务器 {} PowerShell版本检查错误输出: {}", server.getHostname(), result.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("[PS检查错误] 检查Windows服务器 {} 的PowerShell版本时出错: {}", server.getHostname(), e.getMessage());
        }
        
        // 检查必要的工具是否存在
        String[] requiredTools = {"robocopy", "wbadmin"};
        for (String tool : requiredTools) {
            try {
                String checkCommand = "where " + tool;
                SshService.CommandResult result = sshService.executeCommand(server, checkCommand);
                if (!result.isSuccess()) {
                    log.warn("[工具缺失] Windows服务器 {} 上未找到工具: {}", server.getHostname(), tool);
                } else {
                    log.info("[工具存在] Windows服务器 {} 上找到工具: {}", server.getHostname(), tool);
                }
            } catch (Exception e) {
                log.warn("[检查错误] 检查Windows服务器 {} 上的工具 {} 时出错: {}", server.getHostname(), tool, e.getMessage());
            }
        }
        
        // 检查并安装rclone
        checkAndInstallRclone(server);
    }
    
    /**
     * 执行全系统迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performFullSystemMigration(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[全系统] 执行全系统迁移: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            
            // 获取用户选择的磁盘列表
            List<String> selectedDisks = getSelectedDisks(task);
            
            // 根据源服务器操作系统类型执行不同的迁移方法
            if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                log.info("[Linux全系统] 执行Linux全系统迁移");
                performLinuxFullSystemMigration(task, sourceServer, targetServer, selectedDisks);
            } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                log.info("[Windows全系统] 执行Windows全系统迁移");
                performWindowsFullSystemMigration(task, sourceServer, targetServer, selectedDisks);
            } else {
                // 默认迁移方法
                log.info("[默认全系统] 执行默认全系统迁移");
                performDefaultFullSystemMigration(task, sourceServer, targetServer, selectedDisks);
            }
        } catch (Exception e) {
            log.error("[全系统失败] 全系统迁移失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            throw new RuntimeException("全系统迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 获取用户选择的磁盘列表
     * @param task 迁移任务
     * @return 选择的磁盘列表
     */
    private List<String> getSelectedDisks(MigrationTask task) {
        List<String> selectedDisks = new ArrayList<>();
        if (task.getSelectedDisks() != null && !task.getSelectedDisks().isEmpty()) {
            try {
                selectedDisks = objectMapper.readValue(task.getSelectedDisks(), new TypeReference<List<String>>() {});
                log.info("[磁盘选择] 解析用户选择的磁盘列表: {}", selectedDisks);
            } catch (Exception e) {
                log.warn("[磁盘解析错误] 解析选择的磁盘列表失败: {}", e.getMessage());
            }
        }
        return selectedDisks;
    }
    
    /**
     * 执行Linux全系统迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     * @param selectedDisks 选择的磁盘列表
     */
    private void performLinuxFullSystemMigration(MigrationTask task, Server sourceServer, Server targetServer, List<String> selectedDisks) {
        log.info("[Linux迁移] 执行Linux全系统迁移: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        
        try {
            // 如果用户没有选择磁盘，默认迁移系统盘
            if (selectedDisks.isEmpty()) {
                selectedDisks.add("sda"); // 默认系统盘
                log.info("[默认磁盘] 未选择磁盘，使用默认系统盘: sda");
            }
            
            log.info("[磁盘准备] 准备迁移磁盘: {}", selectedDisks);
            
            // 步骤1: 创建系统快照 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[快照创建] 步骤1: 创建Linux系统快照，磁盘: {}", selectedDisks);
            
            // 对于Linux全系统迁移，使用rclone直接传输整个文件系统
            // 我们将传输根目录，但排除一些不需要的目录
            String excludeOptions = "--exclude=/tmp --exclude=/proc --exclude=/dev --exclude=/sys --exclude=/run --exclude=/var/tmp";
            
            // 步骤2: 传输系统快照 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[快照传输] 步骤2: 传输Linux系统快照");
            
            // 使用rclone传输整个文件系统（替换原来的dd和scp命令）
            String transferCommand = String.format(
                "rclone copy / %s@%s:/ --sftp-port=%d --sftp-pass=\"%s\" %s", 
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                targetServer.getPort(),
                targetServer.getPassword(),
                excludeOptions
            );
            log.info("[传输命令] 执行传输命令: {}", transferCommand);
            SshService.CommandResult transferResult = sshService.executeCommand(sourceServer, transferCommand);
            if (!transferResult.isSuccess()) {
                log.error("[传输失败] 传输系统快照失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), transferResult.getMessage());
                // 打印错误输出（如果有的话）
                if (transferResult.getMessage() != null && !transferResult.getMessage().isEmpty()) {
                    log.error("[传输错误详情] 系统快照传输错误输出: {}", transferResult.getMessage());
                }
                throw new RuntimeException("传输系统快照失败: " + transferResult.getMessage());
            }
            
            // 特别检查输出中是否包含错误信息
            if (transferResult.getOutput() != null && transferResult.getOutput().contains("command not found")) {
                log.error("[命令缺失] rclone命令未找到，传输系统快照失败: {} -> {} - 输出: {}", sourceServer.getHostname(), targetServer.getHostname(), transferResult.getOutput());
                throw new RuntimeException("rclone命令未找到，传输系统快照失败: " + transferResult.getOutput());
            }
            
            log.info("[Linux完成] Linux全系统迁移完成: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        } catch (Exception e) {
            log.error("[Linux失败] Linux全系统迁移失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            throw new RuntimeException("Linux全系统迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行Windows全系统迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     * @param selectedDisks 选择的磁盘列表
     */
    private void performWindowsFullSystemMigration(MigrationTask task, Server sourceServer, Server targetServer, List<String> selectedDisks) {
        log.info("[Windows迁移] 执行Windows全系统迁移: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        
        try {
            // 如果用户没有选择磁盘，默认迁移C盘
            if (selectedDisks.isEmpty()) {
                selectedDisks.add("C:"); // 默认系统盘
                log.info("[默认磁盘] 未选择磁盘，使用默认系统盘: C:");
            }
            
            log.info("[磁盘准备] 准备迁移磁盘: {}", selectedDisks);
            
            // 步骤1: 创建系统快照 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[快照创建] 步骤1: 创建Windows系统快照，磁盘: {}", selectedDisks);
            
            // 对于Windows全系统迁移，使用rclone传输整个系统盘
            // 我们将传输C盘根目录
            String excludeOptions = "--exclude=Windows\\Temp --exclude=Windows\\System32\\config\\RegBack";
            
            // 步骤2: 传输系统快照 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[快照传输] 步骤2: 传输Windows系统快照");
            
            // 使用rclone传输整个C盘（替换原来的wbadmin命令）
            String transferCommand = String.format(
                "rclone copy %s\\ %s@%s:%s\\ --sftp-port=%d --sftp-pass=\"%s\" %s", 
                selectedDisks.get(0),
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                selectedDisks.get(0),
                targetServer.getPort(),
                targetServer.getPassword(),
                excludeOptions
            );
            log.info("[传输命令] 执行传输命令: {}", transferCommand);
            SshService.CommandResult transferResult = sshService.executeCommand(sourceServer, transferCommand);
            if (!transferResult.isSuccess()) {
                log.error("[传输失败] 传输系统快照失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), transferResult.getMessage());
                // 打印错误输出（如果有的话）
                if (transferResult.getMessage() != null && !transferResult.getMessage().isEmpty()) {
                    log.error("[传输错误详情] 系统快照传输错误输出: {}", transferResult.getMessage());
                }
                throw new RuntimeException("传输系统快照失败: " + transferResult.getMessage());
            }
            
            log.info("[传输成功] 成功传输磁盘 {} 备份: {} -> {}", selectedDisks.get(0), sourceServer.getHostname(), targetServer.getHostname());
            
            log.info("[Windows完成] Windows全系统迁移完成: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        } catch (Exception e) {
            log.error("[Windows失败] Windows全系统迁移失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            throw new RuntimeException("Windows全系统迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行默认全系统迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     * @param selectedDisks 选择的磁盘列表
     */
    private void performDefaultFullSystemMigration(MigrationTask task, Server sourceServer, Server targetServer, List<String> selectedDisks) {
        log.info("[默认迁移] 执行默认全系统迁移: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        
        try {
            // 如果用户没有选择磁盘，默认迁移根目录
            if (selectedDisks.isEmpty()) {
                // 对于默认情况，我们仍然使用原来的根目录备份方法
                selectedDisks = Arrays.asList("/"); // 默认根目录
                log.info("[默认路径] 未选择磁盘，使用默认根目录: /");
            }
            
            // 步骤1: 创建系统快照 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[快照创建] 步骤1: 创建系统快照");
            
            // 步骤2: 传输系统快照 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[快照传输] 步骤2: 传输系统快照");
            
            // 使用rclone传输整个根目录（替换原来的tar和scp命令）
            String excludeOptions = "--exclude=/tmp --exclude=/proc --exclude=/dev --exclude=/sys --exclude=/run --exclude=/var/tmp";
            
            String transferCommand = String.format(
                "rclone copy / %s@%s:/ --sftp-port=%d --sftp-pass=\"%s\" %s", 
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                targetServer.getPort(),
                targetServer.getPassword(),
                excludeOptions
            );
            SshService.CommandResult transferResult = sshService.executeCommand(sourceServer, transferCommand);
            if (!transferResult.isSuccess()) {
                log.error("[传输失败] 传输系统快照失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), transferResult.getMessage());
                // 打印错误输出（如果有的话）
                if (transferResult.getMessage() != null && !transferResult.getMessage().isEmpty()) {
                    log.error("[传输错误详情] 系统快照传输错误输出: {}", transferResult.getMessage());
                }
                throw new RuntimeException("传输系统快照失败: " + transferResult.getMessage());
            }
            
            log.info("[默认完成] 默认全系统迁移完成: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        } catch (Exception e) {
            log.error("[默认失败] 默认全系统迁移失败: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            throw new RuntimeException("默认全系统迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行部分目录迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performPartialMigration(MigrationTask task, Server sourceServer, Server targetServer) {
        log.info("[部分迁移] 执行部分目录迁移: {}:{} -> {}:{}", 
                sourceServer.getHostname(), task.getSourcePath(), 
                targetServer.getHostname(), task.getTargetPath());
        
        // 验证路径字段对于部分目录迁移是必需的
        if (task.getSourcePath() == null || task.getSourcePath().trim().isEmpty()) {
            String errorMsg = "源路径不能为空";
            log.error("[路径错误] {} - 任务: {} (ID: {})", errorMsg, task.getTaskName(), task.getId());
            throw new RuntimeException(errorMsg);
        }
        if (task.getTargetPath() == null || task.getTargetPath().trim().isEmpty()) {
            String errorMsg = "目标路径不能为空";
            log.error("[路径错误] {} - 任务: {} (ID: {})", errorMsg, task.getTaskName(), task.getId());
            throw new RuntimeException(errorMsg);
        }
        
        try {
            // 根据源服务器操作系统类型执行不同的迁移方法
            if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                log.info("[Linux部分] 执行Linux部分目录迁移");
                performLinuxPartialMigration(task, sourceServer, targetServer);
            } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                log.info("[Windows部分] 执行Windows部分目录迁移");
                performWindowsPartialMigration(task, sourceServer, targetServer);
            } else {
                // 默认迁移方法
                log.info("[默认部分] 执行默认部分目录迁移");
                performDefaultPartialMigration(task, sourceServer, targetServer);
            }
        } catch (Exception e) {
            log.error("[部分失败] 部分目录迁移失败: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage(), e);
            throw new RuntimeException("部分目录迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行Linux部分目录迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performLinuxPartialMigration(MigrationTask task, Server sourceServer, Server targetServer) {
        log.info("[Linux目录] 执行Linux部分目录迁移: {}:{} -> {}:{}", 
                sourceServer.getHostname(), task.getSourcePath(), 
                targetServer.getHostname(), task.getTargetPath());
        
        try {
            // 步骤1: 扫描目录结构 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[目录扫描] 步骤1: 扫描Linux目录结构: {}", task.getSourcePath());
            
            // 检查源目录是否存在
            String checkSourceCommand = "test -d " + task.getSourcePath() + " && echo 'exists' || echo 'not exists'";
            log.info("[目录检查] 检查源目录是否存在: {}", checkSourceCommand);
            SshService.CommandResult checkResult = sshService.executeCommand(sourceServer, checkSourceCommand);
            if (!checkResult.isSuccess() || "not exists".equals(checkResult.getOutput().trim())) {
                log.error("[目录缺失] 源目录不存在: {} - 服务器: {}", task.getSourcePath(), sourceServer.getHostname());
                // 打印错误输出（如果有的话）
                if (checkResult.getMessage() != null && !checkResult.getMessage().isEmpty()) {
                    log.error("[目录错误详情] 源目录检查错误输出: {}", checkResult.getMessage());
                }
                throw new RuntimeException("源目录不存在: " + task.getSourcePath());
            }
            
            // 使用find命令扫描目录
            String scanCommand = "find " + task.getSourcePath() + " -type f | wc -l";
            log.info("[文件统计] 扫描目录文件数量: {}", scanCommand);
            SshService.CommandResult scanResult = sshService.executeCommand(sourceServer, scanCommand);
            if (!scanResult.isSuccess()) {
                log.error("[扫描失败] 扫描目录结构失败: {} - 服务器: {} - 错误信息: {}", task.getSourcePath(), sourceServer.getHostname(), scanResult.getMessage());
                // 打印错误输出（如果有的话）
                if (scanResult.getMessage() != null && !scanResult.getMessage().isEmpty()) {
                    log.error("[扫描错误详情] 目录扫描错误输出: {}", scanResult.getMessage());
                }
                throw new RuntimeException("扫描目录结构失败: " + scanResult.getMessage());
            }
            log.info("[文件数量] 源目录包含 {} 个文件: {} - 服务器: {}", scanResult.getOutput().trim(), task.getSourcePath(), sourceServer.getHostname());
            
            // 步骤2: 传输目录数据 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[数据传输] 步骤2: 传输Linux目录数据: {} -> {}", task.getSourcePath(), task.getTargetPath());
            
            // 使用rclone传输目录数据（替换原来的rsync命令）
            String syncCommand = String.format(
                "rclone copy %s/ %s@%s:%s/ --sftp-port=%d --sftp-pass=\"%s\"", 
                task.getSourcePath(), 
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                task.getTargetPath(),
                targetServer.getPort(),
                targetServer.getPassword()
            );
            log.info("[传输命令] 执行传输命令: {}", syncCommand);
            SshService.CommandResult syncResult = sshService.executeCommand(sourceServer, syncCommand);
            
            // 检查rclone命令是否真正成功执行
            if (!syncResult.isSuccess()) {
                log.error("[传输失败] 传输目录数据失败: {}:{} -> {}:{} - 错误信息: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getMessage());
                // 打印错误输出（如果有的话）
                if (syncResult.getMessage() != null && !syncResult.getMessage().isEmpty()) {
                    log.error("[传输错误详情] 数据传输错误输出: {}", syncResult.getMessage());
                }
                throw new RuntimeException("传输目录数据失败: " + syncResult.getMessage());
            }
            
            // 特别检查输出中是否包含错误信息
            if (syncResult.getOutput() != null && syncResult.getOutput().contains("command not found")) {
                log.error("[命令缺失] rclone命令未找到，传输目录数据失败: {}:{} -> {}:{} - 输出: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getOutput());
                throw new RuntimeException("rclone命令未找到，传输目录数据失败: " + syncResult.getOutput());
            }
            
            log.info("[Linux完成] Linux部分目录迁移完成: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
        } catch (Exception e) {
            log.error("[Linux失败] Linux部分目录迁移失败: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage(), e);
            throw new RuntimeException("Linux部分目录迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行Windows部分目录迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performWindowsPartialMigration(MigrationTask task, Server sourceServer, Server targetServer) {
        log.info("[Windows目录] 执行Windows部分目录迁移: {}:{} -> {}:{}", 
                sourceServer.getHostname(), task.getSourcePath(), 
                targetServer.getHostname(), task.getTargetPath());
        
        try {
            // 步骤1: 扫描目录结构 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[目录扫描] 步骤1: 扫描Windows目录结构: {}", task.getSourcePath());
            
            // 检查源目录是否存在
            String checkSourceCommand = String.format(
                "if exist \"%s\" (echo exists) else (echo not exists)", 
                task.getSourcePath()
            );
            log.info("[目录检查] 检查源目录是否存在: {}", checkSourceCommand);
            SshService.CommandResult checkResult = sshService.executeCommand(sourceServer, checkSourceCommand);
            if (!checkResult.isSuccess() || "not exists".equals(checkResult.getOutput().trim())) {
                log.error("[目录缺失] 源目录不存在: {} - 服务器: {}", task.getSourcePath(), sourceServer.getHostname());
                // 打印错误输出（如果有的话）
                if (checkResult.getMessage() != null && !checkResult.getMessage().isEmpty()) {
                    log.error("[目录错误详情] 源目录检查错误输出: {}", checkResult.getMessage());
                }
                throw new RuntimeException("源目录不存在: " + task.getSourcePath());
            }
            
            // 步骤2: 传输目录数据 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[数据传输] 步骤2: 传输Windows目录数据: {} -> {}", task.getSourcePath(), task.getTargetPath());
            
            // 使用rclone传输目录数据（替换原来的robocopy命令）
            String syncCommand = String.format(
                "rclone copy \"%s\" %s@%s:%s --sftp-port=%d --sftp-pass=\"%s\"", 
                task.getSourcePath(), 
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                task.getTargetPath().replace("/", "\\"),
                targetServer.getPort(),
                targetServer.getPassword()
            );
            log.info("[传输命令] 执行传输命令: {}", syncCommand);
            SshService.CommandResult syncResult = sshService.executeCommand(sourceServer, syncCommand);
            
            // 检查rclone命令是否真正成功执行
            if (!syncResult.isSuccess()) {
                // 现在正确抛出异常，而不是只记录警告
                log.error("[rclone失败] rclone执行失败: {}:{} -> {}:{} - 错误信息: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getMessage());
                // 打印错误输出（如果有的话）
                if (syncResult.getMessage() != null && !syncResult.getMessage().isEmpty()) {
                    log.error("[rclone错误详情] rclone执行错误输出: {}", syncResult.getMessage());
                }
                throw new RuntimeException("传输目录数据失败: " + syncResult.getMessage());
            }
            
            // 特别检查输出中是否包含错误信息
            if (syncResult.getOutput() != null && syncResult.getOutput().contains("command not found")) {
                log.error("[命令缺失] rclone命令未找到，传输目录数据失败: {}:{} -> {}:{} - 输出: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getOutput());
                throw new RuntimeException("rclone命令未找到，传输目录数据失败: " + syncResult.getOutput());
            }
            
            log.info("[Windows完成] Windows部分目录迁移完成: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
        } catch (Exception e) {
            log.error("[Windows失败] Windows部分目录迁移失败: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage(), e);
            throw new RuntimeException("Windows部分目录迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行默认部分目录迁移
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void performDefaultPartialMigration(MigrationTask task, Server sourceServer, Server targetServer) {
        log.info("[默认目录] 执行默认部分目录迁移: {}:{} -> {}:{}", 
                sourceServer.getHostname(), task.getSourcePath(), 
                targetServer.getHostname(), task.getTargetPath());
        
        try {
            // 步骤1: 扫描目录结构 (30%)
            updateTaskStatus(task.getId(), "RUNNING", 30, null);
            log.info("[目录扫描] 步骤1: 扫描目录结构: {}", task.getSourcePath());
            
            // 检查源目录是否存在
            String checkSourceCommand = "test -d " + task.getSourcePath() + " && echo 'exists' || echo 'not exists'";
            log.info("[目录检查] 检查源目录是否存在: {}", checkSourceCommand);
            SshService.CommandResult checkResult = sshService.executeCommand(sourceServer, checkSourceCommand);
            if (!checkResult.isSuccess() || "not exists".equals(checkResult.getOutput().trim())) {
                log.error("[目录缺失] 源目录不存在: {} - 服务器: {}", task.getSourcePath(), sourceServer.getHostname());
                // 打印错误输出（如果有的话）
                if (checkResult.getMessage() != null && !checkResult.getMessage().isEmpty()) {
                    log.error("[目录错误详情] 源目录检查错误输出: {}", checkResult.getMessage());
                }
                throw new RuntimeException("源目录不存在: " + task.getSourcePath());
            }
            
            // 步骤2: 传输目录数据 (80%)
            updateTaskStatus(task.getId(), "RUNNING", 80, null);
            log.info("[数据传输] 步骤2: 传输目录数据: {} -> {}", task.getSourcePath(), task.getTargetPath());
            
            // 使用rclone传输目录数据（替换原来的scp命令）
            String syncCommand = String.format(
                "rclone copy %s/ %s@%s:%s/ --sftp-port=%d --sftp-pass=\"%s\"", 
                task.getSourcePath(), 
                targetServer.getUsername(), 
                targetServer.getHostname(), 
                task.getTargetPath(),
                targetServer.getPort(),
                targetServer.getPassword()
            );
            log.info("[传输命令] 执行传输命令: {}", syncCommand);
            SshService.CommandResult syncResult = sshService.executeCommand(sourceServer, syncCommand);
            
            // 检查rclone命令是否真正成功执行
            if (!syncResult.isSuccess()) {
                log.error("[传输失败] 传输目录数据失败: {}:{} -> {}:{} - 错误信息: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getMessage());
                // 打印错误输出（如果有的的话）
                if (syncResult.getMessage() != null && !syncResult.getMessage().isEmpty()) {
                    log.error("[传输错误详情] 数据传输错误输出: {}", syncResult.getMessage());
                }
                throw new RuntimeException("传输目录数据失败: " + syncResult.getMessage());
            }
            
            // 特别检查输出中是否包含错误信息
            if (syncResult.getOutput() != null && syncResult.getOutput().contains("command not found")) {
                log.error("[命令缺失] rclone命令未找到，传输目录数据失败: {}:{} -> {}:{} - 输出: {}", 
                        sourceServer.getHostname(), task.getSourcePath(), 
                        targetServer.getHostname(), task.getTargetPath(), 
                        syncResult.getOutput());
                throw new RuntimeException("rclone命令未找到，传输目录数据失败: " + syncResult.getOutput());
            }
            
            log.info("[默认完成] 默认部分目录迁移完成: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
        } catch (Exception e) {
            log.error("[默认失败] 默认部分目录迁移失败: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage(), e);
            throw new RuntimeException("默认部分目录迁移失败: " + e.getMessage());
        }
    }
    
    /**
     * 清理临时文件
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void cleanupTemporaryFiles(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[清理开始] 开始清理临时文件: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            
            // 根据迁移类型执行不同的清理操作
            if ("FULL_SYSTEM".equals(task.getMigrationType())) {
                cleanupFullSystemMigrationFiles(task, sourceServer, targetServer);
            } else {
                cleanupPartialMigrationFiles(task, sourceServer, targetServer);
            }
            
            log.info("[清理完成] 临时文件清理完成: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
        } catch (Exception e) {
            log.warn("[清理警告] 清理临时文件时出现警告: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage());
            // 清理失败不应该影响整体迁移结果，所以只记录警告
        }
    }
    
    /**
     * 验证迁移结果
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void verifyMigrationResult(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[验证开始] 开始验证迁移结果: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            
            boolean isMigrationSuccessful = false;
            
            // 根据迁移类型执行不同的验证操作
            if ("FULL_SYSTEM".equals(task.getMigrationType())) {
                isMigrationSuccessful = verifyFullSystemMigrationResult(task, sourceServer, targetServer);
            } else {
                isMigrationSuccessful = verifyPartialMigrationResult(task, sourceServer, targetServer);
            }
            
            if (isMigrationSuccessful) {
                log.info("[验证成功] 迁移结果验证成功: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            } else {
                String errorMsg = "迁移结果验证失败";
                log.error("[验证失败] {} - 服务器: {} -> {}", errorMsg, sourceServer.getHostname(), targetServer.getHostname());
                throw new RuntimeException(errorMsg);
            }
        } catch (Exception e) {
            log.error("[验证错误] 验证迁移结果时出错: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            throw new RuntimeException("迁移结果验证失败: " + e.getMessage());
        }
    }
    
    /**
     * 验证全系统迁移结果
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     * @return 验证是否成功
     */
    private boolean verifyFullSystemMigrationResult(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[全系统验证] 验证全系统迁移结果: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            
            // 获取用户选择的磁盘列表
            List<String> selectedDisks = getSelectedDisks(task);
            
            // 如果用户没有选择磁盘，使用默认磁盘
            if (selectedDisks.isEmpty()) {
                if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                    selectedDisks.add("sda"); // 默认Linux系统盘
                } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                    selectedDisks.add("C:"); // 默认Windows系统盘
                } else {
                    selectedDisks.add("/"); // 默认根目录
                }
                log.info("[默认磁盘] 未选择磁盘，使用默认磁盘: {}", selectedDisks);
            }
            
            // 验证目标服务器上是否存在迁移的磁盘/目录
            for (String disk : selectedDisks) {
                if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                    // 对于Linux，检查根目录是否存在关键系统目录
                    String checkCommand = "test -d /bin && test -d /etc && test -d /usr && echo 'success' || echo 'failed'";
                    SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                    if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                        log.error("[Linux验证失败] Linux系统目录验证失败: {} -> {} - 输出: {}", sourceServer.getHostname(), targetServer.getHostname(), result.getOutput());
                        return false;
                    }
                } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                    // 对于Windows，检查C盘是否存在关键系统目录
                    String checkCommand = String.format(
                        "if exist \"%s\\Windows\" if exist \"%s\\Program Files\" (echo success) else (echo failed)", 
                        disk, disk
                    );
                    SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                    if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                        log.error("[Windows验证失败] Windows系统目录验证失败: {} -> {} - 输出: {}", sourceServer.getHostname(), targetServer.getHostname(), result.getOutput());
                        return false;
                    }
                } else {
                    // 对于默认情况，检查根目录是否存在
                    String checkCommand = "test -d /bin && test -d /etc && test -d /usr && echo 'success' || echo 'failed'";
                    SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                    if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                        log.error("[默认验证失败] 默认系统目录验证失败: {} -> {} - 输出: {}", sourceServer.getHostname(), targetServer.getHostname(), result.getOutput());
                        return false;
                    }
                }
            }
            
            log.info("[全系统验证成功] 全系统迁移结果验证成功: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            return true;
        } catch (Exception e) {
            log.error("[全系统验证错误] 验证全系统迁移结果时出错: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 验证部分目录迁移结果
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     * @return 验证是否成功
     */
    private boolean verifyPartialMigrationResult(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[部分验证] 验证部分目录迁移结果: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
            
            // 检查目标路径是否存在
            if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                String checkCommand = "test -d " + task.getTargetPath() + " && echo 'success' || echo 'failed'";
                SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                    log.error("[目标缺失] 目标目录不存在: {} - 服务器: {}", task.getTargetPath(), targetServer.getHostname());
                    return false;
                }
            } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                String checkCommand = String.format(
                    "if exist \"%s\" (echo success) else (echo failed)", 
                    task.getTargetPath()
                );
                SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                    log.error("[目标缺失] 目标目录不存在: {} - 服务器: {}", task.getTargetPath(), targetServer.getHostname());
                    return false;
                }
            } else {
                String checkCommand = "test -d " + task.getTargetPath() + " && echo 'success' || echo 'failed'";
                SshService.CommandResult result = sshService.executeCommand(targetServer, checkCommand);
                if (!result.isSuccess() || !"success".equals(result.getOutput().trim())) {
                    log.error("[目标缺失] 目标目录不存在: {} - 服务器: {}", task.getTargetPath(), targetServer.getHostname());
                    return false;
                }
            }
            
            // 检查目标路径是否包含文件
            if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                String countCommand = "find " + task.getTargetPath() + " -type f | wc -l";
                SshService.CommandResult result = sshService.executeCommand(targetServer, countCommand);
                if (result.isSuccess()) {
                    try {
                        int fileCount = Integer.parseInt(result.getOutput().trim());
                        if (fileCount <= 0) {
                            log.error("[目标空] 目标目录为空: {} - 服务器: {}", task.getTargetPath(), targetServer.getHostname());
                            return false;
                        }
                        log.info("[文件统计] 目标目录包含 {} 个文件: {} - 服务器: {}", fileCount, task.getTargetPath(), targetServer.getHostname());
                    } catch (NumberFormatException e) {
                        log.warn("[计数错误] 无法解析文件数量: {} - 服务器: {} - 输出: {}", task.getTargetPath(), targetServer.getHostname(), result.getOutput());
                    }
                }
            } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                String countCommand = String.format(
                    "powershell -Command \"(Get-ChildItem -Path '%s' -File -Recurse | Measure-Object).Count\"", 
                    task.getTargetPath()
                );
                SshService.CommandResult result = sshService.executeCommand(targetServer, countCommand);
                if (result.isSuccess()) {
                    try {
                        int fileCount = Integer.parseInt(result.getOutput().trim());
                        if (fileCount <= 0) {
                            log.error("[目标空] 目标目录为空: {} - 服务器: {}", task.getTargetPath(), targetServer.getHostname());
                            return false;
                        }
                        log.info("[文件统计] 目标目录包含 {} 个文件: {} - 服务器: {}", fileCount, task.getTargetPath(), targetServer.getHostname());
                    } catch (NumberFormatException e) {
                        log.warn("[计数错误] 无法解析文件数量: {} - 服务器: {} - 输出: {}", task.getTargetPath(), targetServer.getHostname(), result.getOutput());
                    }
                }
            }
            
            log.info("[部分验证成功] 部分目录迁移结果验证成功: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
            return true;
        } catch (Exception e) {
            log.error("[部分验证错误] 验证部分目录迁移结果时出错: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage(), e);
            return false;
        }
    }
    
    /**
     * 清理全系统迁移的临时文件
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void cleanupFullSystemMigrationFiles(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[全系统清理] 清理全系统迁移的临时文件: {} -> {}", sourceServer.getHostname(), targetServer.getHostname());
            
            // 清理源服务器上的临时文件（如果有的话）
            String cleanupSourceCommand = "rm -f /tmp/migration_*.tmp";
            SshService.CommandResult sourceResult = sshService.executeCommand(sourceServer, cleanupSourceCommand);
            if (sourceResult.isSuccess()) {
                log.info("[源清理完成] 源服务器临时文件清理完成: {}", sourceServer.getHostname());
            } else {
                log.warn("[源清理失败] 源服务器临时文件清理失败: {} - 错误信息: {}", sourceServer.getHostname(), sourceResult.getMessage());
            }
            
            // 清理目标服务器上的临时文件（如果有的话）
            String cleanupTargetCommand = "rm -f /tmp/migration_*.tmp";
            SshService.CommandResult targetResult = sshService.executeCommand(targetServer, cleanupTargetCommand);
            if (targetResult.isSuccess()) {
                log.info("[目标清理完成] 目标服务器临时文件清理完成: {}", targetServer.getHostname());
            } else {
                log.warn("[目标清理失败] 目标服务器临时文件清理失败: {} - 错误信息: {}", targetServer.getHostname(), targetResult.getMessage());
            }
        } catch (Exception e) {
            log.warn("[全系统清理警告] 清理全系统迁移临时文件时出现警告: {} -> {} - 错误信息: {}", sourceServer.getHostname(), targetServer.getHostname(), e.getMessage());
        }
    }
    
    /**
     * 清理部分目录迁移的临时文件
     * @param task 迁移任务
     * @param sourceServer 源服务器
     * @param targetServer 目标服务器
     */
    private void cleanupPartialMigrationFiles(MigrationTask task, Server sourceServer, Server targetServer) {
        try {
            log.info("[部分清理] 清理部分目录迁移的临时文件: {}:{} -> {}:{}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath());
            
            // 清理源服务器上的临时文件（如果有的话）
            if ("LINUX".equalsIgnoreCase(sourceServer.getOsType())) {
                String cleanupSourceCommand = "rm -f " + task.getSourcePath() + "/migration_*.tmp";
                SshService.CommandResult sourceResult = sshService.executeCommand(sourceServer, cleanupSourceCommand);
                if (sourceResult.isSuccess()) {
                    log.info("[源清理完成] 源服务器临时文件清理完成: {}:{}", sourceServer.getHostname(), task.getSourcePath());
                } else {
                    log.warn("[源清理失败] 源服务器临时文件清理失败: {}:{} - 错误信息: {}", sourceServer.getHostname(), task.getSourcePath(), sourceResult.getMessage());
                }
            } else if ("WINDOWS".equalsIgnoreCase(sourceServer.getOsType())) {
                String cleanupSourceCommand = String.format(
                    "del \"%s\\migration_*.tmp\"", 
                    task.getSourcePath()
                );
                SshService.CommandResult sourceResult = sshService.executeCommand(sourceServer, cleanupSourceCommand);
                if (sourceResult.isSuccess()) {
                    log.info("[源清理完成] 源服务器临时文件清理完成: {}:{}", sourceServer.getHostname(), task.getSourcePath());
                } else {
                    log.warn("[源清理失败] 源服务器临时文件清理失败: {}:{} - 错误信息: {}", sourceServer.getHostname(), task.getSourcePath(), sourceResult.getMessage());
                }
            }
            
            // 清理目标服务器上的临时文件（如果有的话）
            if ("LINUX".equalsIgnoreCase(targetServer.getOsType())) {
                String cleanupTargetCommand = "rm -f " + task.getTargetPath() + "/migration_*.tmp";
                SshService.CommandResult targetResult = sshService.executeCommand(targetServer, cleanupTargetCommand);
                if (targetResult.isSuccess()) {
                    log.info("[目标清理完成] 目标服务器临时文件清理完成: {}:{}", targetServer.getHostname(), task.getTargetPath());
                } else {
                    log.warn("[目标清理失败] 目标服务器临时文件清理失败: {}:{} - 错误信息: {}", targetServer.getHostname(), task.getTargetPath(), targetResult.getMessage());
                }
            } else if ("WINDOWS".equalsIgnoreCase(targetServer.getOsType())) {
                String cleanupTargetCommand = String.format(
                    "del \"%s\\migration_*.tmp\"", 
                    task.getTargetPath()
                );
                SshService.CommandResult targetResult = sshService.executeCommand(targetServer, cleanupTargetCommand);
                if (targetResult.isSuccess()) {
                    log.info("[目标清理完成] 目标服务器临时文件清理完成: {}:{}", targetServer.getHostname(), task.getTargetPath());
                } else {
                    log.warn("[目标清理失败] 目标服务器临时文件清理失败: {}:{} - 错误信息: {}", targetServer.getHostname(), task.getTargetPath(), targetResult.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("[部分清理警告] 清理部分目录迁移临时文件时出现警告: {}:{} -> {}:{} - 错误信息: {}", 
                    sourceServer.getHostname(), task.getSourcePath(), 
                    targetServer.getHostname(), task.getTargetPath(), 
                    e.getMessage());
        }
    }
    
    /**
     * 检查并安装rclone
     * @param server 服务器
     */
    private void checkAndInstallRclone(Server server) {
        try {
            log.info("[rclone检查] 检查服务器 {} 上是否安装了rclone", server.getHostname());
            
            // 使用新的可靠检查方法
            SshService.CommandResult checkResult = sshService.checkRcloneInstalled(server);
            
            if (checkResult.isSuccess()) {
                log.info("[rclone已安装] 服务器 {} 上已正确安装rclone", server.getHostname());
            } else {
                log.warn("[rclone缺失] 服务器 {} 上未正确安装rclone，准备安装...", server.getHostname());
                // 执行安装过程并等待结果
                boolean installSuccess = installRcloneAndWait(server);
                
                if (installSuccess) {
                    // 安装后再次验证
                    SshService.CommandResult verifyResult = sshService.checkRcloneInstalled(server);
                    if (verifyResult.isSuccess()) {
                        log.info("[rclone验证成功] 服务器 {} 上rclone安装验证成功", server.getHostname());
                    } else {
                        log.error("[rclone验证失败] 服务器 {} 上rclone安装验证失败", server.getHostname());
                        // 即使验证失败也记录但不中断流程
                        log.warn("[rclone警告] rclone安装验证失败，但将继续执行迁移任务: {}", server.getHostname());
                    }
                } else {
                    log.error("[rclone安装失败] 服务器 {} 上rclone安装失败", server.getHostname());
                    // 即使安装失败也记录但不中断流程
                    log.warn("[rclone警告] rclone安装失败，但将继续执行迁移任务: {}", server.getHostname());
                }
            }
        } catch (Exception e) {
            log.error("[rclone检查错误] 检查服务器 {} 上rclone安装状态时出错: {}", server.getHostname(), e.getMessage(), e);
            // 即使检查出错也继续执行，避免阻断迁移流程
            log.warn("[rclone警告] rclone检查过程中出现异常，但将继续执行迁移任务: {} - 错误信息: {}", server.getHostname(), e.getMessage());
        }
    }
    
    /**
     * 安装rclone并等待结果
     * @param server 服务器
     * @return 是否安装成功
     */
    private boolean installRcloneAndWait(Server server) {
        try {
            log.info("[rclone安装] 开始在服务器 {} 上安装rclone", server.getHostname());
            
            // 首先下载rclone到本地项目目录
            downloadRcloneToProject();
            
            // 然后传输安装脚本到服务器并执行
            SshService.CommandResult result;
            if ("LINUX".equalsIgnoreCase(server.getOsType())) {
                log.info("[Linux安装] 在Linux服务器 {} 上安装rclone", server.getHostname());
                // 先传输rclone安装包
                File packageFile = new File(RCLONE_LINUX_PACKAGE);
                if (packageFile.exists()) {
                    log.info("[安装包传输] 传输rclone安装包到服务器 {}:{}", server.getHostname(), "/tmp/rclone-current-linux-amd64.zip");
                    SshService.CommandResult transferResult = sshService.transferFileToServer(server, RCLONE_LINUX_PACKAGE, "/tmp/rclone-current-linux-amd64.zip");
                    if (transferResult.isSuccess()) {
                        log.info("[传输成功] rclone安装包传输成功: {} -> {}", server.getHostname(), "/tmp/rclone-current-linux-amd64.zip");
                    } else {
                        log.warn("[传输失败] rclone安装包传输失败: {} - 错误信息: {}", server.getHostname(), transferResult.getMessage());
                        // 如果传输失败，尝试直接下载
                        log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                        String downloadCommand = "curl -L https://downloads.rclone.org/rclone-current-linux-amd64.zip -o /tmp/rclone-current-linux-amd64.zip";
                        SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                        if (!downloadResult.isSuccess()) {
                            log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                            return false;
                        }
                    }
                } else {
                    log.warn("[本地包缺失] 本地rclone安装包不存在: {} - 服务器: {}", RCLONE_LINUX_PACKAGE, server.getHostname());
                    // 如果本地包不存在，尝试直接下载
                    log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                    String downloadCommand = "curl -L https://downloads.rclone.org/rclone-current-linux-amd64.zip -o /tmp/rclone-current-linux-amd64.zip";
                    SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                    if (!downloadResult.isSuccess()) {
                        log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                        return false;
                    }
                }
                
                // 传输Linux安装脚本到服务器
                result = sshService.transferFileToServer(server, RCLONE_SCRIPT_LINUX, "/tmp/install-rclone-detailed.sh");
                if (!result.isSuccess()) {
                    log.warn("[脚本传输失败] 传输Linux安装脚本失败: {} - 错误信息: {}，尝试使用备用方法", server.getHostname(), result.getMessage());
                    result = installRcloneOnLinux(server);
                } else {
                    // 设置脚本执行权限
                    SshService.CommandResult chmodResult = sshService.executeCommand(server, "chmod +x /tmp/install-rclone-detailed.sh");
                    if (!chmodResult.isSuccess()) {
                        log.warn("[权限设置失败] 设置脚本执行权限失败: {} - 错误信息: {}，尝试直接执行", server.getHostname(), chmodResult.getMessage());
                    }
                    
                    // 执行安装脚本并等待结果
                    log.info("[执行脚本] 在服务器 {} 上执行rclone安装脚本", server.getHostname());
                    result = sshService.executeCommand(server, "bash /tmp/install-rclone-detailed.sh");
                    log.info("[脚本执行完成] 服务器 {} 上rclone安装脚本执行完成，结果: {}", server.getHostname(), result.isSuccess());
                    if (result.getOutput() != null && !result.getOutput().isEmpty()) {
                        log.info("[脚本输出] 服务器 {} 上rclone安装输出: {}", server.getHostname(), result.getOutput());
                    }
                    if (result.getMessage() != null && !result.getMessage().isEmpty()) {
                        log.info("[脚本消息] 服务器 {} 上rclone安装消息: {}", server.getHostname(), result.getMessage());
                    }
                }
            } else if ("WINDOWS".equalsIgnoreCase(server.getOsType())) {
                log.info("[Windows安装] 在Windows服务器 {} 上安装rclone", server.getHostname());
                // 先传输rclone安装包
                File packageFile = new File(RCLONE_WINDOWS_PACKAGE);
                if (packageFile.exists()) {
                    log.info("[安装包传输] 传输rclone安装包到服务器 {}:{}", server.getHostname(), "C:\\temp\\rclone-current-windows-amd64.zip");
                    SshService.CommandResult transferResult = sshService.transferFileToServer(server, RCLONE_WINDOWS_PACKAGE, "C:\\temp\\rclone-current-windows-amd64.zip");
                    if (transferResult.isSuccess()) {
                        log.info("[传输成功] rclone安装包传输成功: {} -> {}", server.getHostname(), "C:\\temp\\rclone-current-windows-amd64.zip");
                    } else {
                        log.warn("[传输失败] rclone安装包传输失败: {} - 错误信息: {}", server.getHostname(), transferResult.getMessage());
                        // 如果传输失败，尝试直接下载
                        log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                        String downloadCommand = "powershell -Command \"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri 'https://downloads.rclone.org/rclone-current-windows-amd64.zip' -OutFile 'C:\\temp\\rclone-current-windows-amd64.zip'\"";
                        SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                        if (!downloadResult.isSuccess()) {
                            log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                            return false;
                        }
                    }
                } else {
                    log.warn("[本地包缺失] 本地rclone安装包不存在: {} - 服务器: {}", RCLONE_WINDOWS_PACKAGE, server.getHostname());
                    // 如果本地包不存在，尝试直接下载
                    log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                    String downloadCommand = "powershell -Command \"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri 'https://downloads.rclone.org/rclone-current-windows-amd64.zip' -OutFile 'C:\\temp\\rclone-current-windows-amd64.zip'\"";
                    SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                    if (!downloadResult.isSuccess()) {
                        log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                        return false;
                    }
                }
                
                // 传输Windows安装脚本到服务器
                result = sshService.transferFileToServer(server, RCLONE_SCRIPT_WINDOWS, "C:\\temp\\install-rclone-windows-local.ps1");
                if (!result.isSuccess()) {
                    log.warn("[脚本传输失败] 传输Windows安装脚本失败: {} - 错误信息: {}，尝试使用备用方法", server.getHostname(), result.getMessage());
                    result = installRcloneOnWindows(server);
                } else {
                    // 执行安装脚本
                    log.info("[执行脚本] 在服务器 {} 上执行rclone安装脚本", server.getHostname());
                    result = sshService.executeCommand(server, "powershell -ExecutionPolicy Bypass -File C:\\temp\\install-rclone-windows-local.ps1");
                    log.info("[脚本执行完成] 服务器 {} 上rclone安装脚本执行完成，结果: {}", server.getHostname(), result.isSuccess());
                    if (result.getOutput() != null && !result.getOutput().isEmpty()) {
                        log.info("[脚本输出] 服务器 {} 上rclone安装输出: {}", server.getHostname(), result.getOutput());
                    }
                    if (result.getMessage() != null && !result.getMessage().isEmpty()) {
                        log.info("[脚本消息] 服务器 {} 上rclone安装消息: {}", server.getHostname(), result.getMessage());
                    }
                }
            } else {
                // 默认使用Linux安装方式
                log.warn("[未知系统] 未知操作系统类型: {}，使用Linux安装方式 - 服务器: {}", server.getOsType(), server.getHostname());
                
                // 先传输rclone安装包
                File packageFile = new File(RCLONE_LINUX_PACKAGE);
                if (packageFile.exists()) {
                    log.info("[安装包传输] 传输rclone安装包到服务器 {}:{}", server.getHostname(), "/tmp/rclone-current-linux-amd64.zip");
                    SshService.CommandResult transferResult = sshService.transferFileToServer(server, RCLONE_LINUX_PACKAGE, "/tmp/rclone-current-linux-amd64.zip");
                    if (transferResult.isSuccess()) {
                        log.info("[传输成功] rclone安装包传输成功: {} -> {}", server.getHostname(), "/tmp/rclone-current-linux-amd64.zip");
                    } else {
                        log.warn("[传输失败] rclone安装包传输失败: {} - 错误信息: {}", server.getHostname(), transferResult.getMessage());
                        // 如果传输失败，尝试直接下载
                        log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                        String downloadCommand = "curl -L https://downloads.rclone.org/rclone-current-linux-amd64.zip -o /tmp/rclone-current-linux-amd64.zip";
                        SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                        if (!downloadResult.isSuccess()) {
                            log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                            return false;
                        }
                    }
                } else {
                    log.warn("[本地包缺失] 本地rclone安装包不存在: {} - 服务器: {}", RCLONE_LINUX_PACKAGE, server.getHostname());
                    // 如果本地包不存在，尝试直接下载
                    log.info("[直接下载] 尝试直接下载rclone安装包到服务器: {}", server.getHostname());
                    String downloadCommand = "curl -L https://downloads.rclone.org/rclone-current-linux-amd64.zip -o /tmp/rclone-current-linux-amd64.zip";
                    SshService.CommandResult downloadResult = sshService.executeCommand(server, downloadCommand);
                    if (!downloadResult.isSuccess()) {
                        log.error("[下载失败] 直接下载rclone安装包失败: {} - 错误信息: {}", server.getHostname(), downloadResult.getMessage());
                        return false;
                    }
                }
                
                // 传输Linux安装脚本到服务器
                result = sshService.transferFileToServer(server, RCLONE_SCRIPT_LINUX, "/tmp/install-rclone-detailed.sh");
                if (!result.isSuccess()) {
                    log.warn("[脚本传输失败] 传输Linux安装脚本失败: {} - 错误信息: {}，尝试使用备用方法", server.getHostname(), result.getMessage());
                    result = installRcloneOnLinux(server);
                } else {
                    // 设置脚本执行权限
                    SshService.CommandResult chmodResult = sshService.executeCommand(server, "chmod +x /tmp/install-rclone-detailed.sh");
                    if (!chmodResult.isSuccess()) {
                        log.warn("[权限设置失败] 设置脚本执行权限失败: {} - 错误信息: {}，尝试直接执行", server.getHostname(), chmodResult.getMessage());
                    }
                    
                    // 执行安装脚本并等待结果
                    log.info("[执行脚本] 在服务器 {} 上执行rclone安装脚本", server.getHostname());
                    result = sshService.executeCommand(server, "bash /tmp/install-rclone-detailed.sh");
                    log.info("[脚本执行完成] 服务器 {} 上rclone安装脚本执行完成，结果: {}", server.getHostname(), result.isSuccess());
                    if (result.getOutput() != null && !result.getOutput().isEmpty()) {
                        log.info("[脚本输出] 服务器 {} 上rclone安装输出: {}", server.getHostname(), result.getOutput());
                    }
                    if (result.getMessage() != null && !result.getMessage().isEmpty()) {
                        log.info("[脚本消息] 服务器 {} 上rclone安装消息: {}", server.getHostname(), result.getMessage());
                    }
                }
            }
            
            if (result.isSuccess()) {
                log.info("[rclone安装成功] rclone安装成功: {}", server.getHostname());
                return true;
            } else {
                log.error("[rclone安装失败] rclone安装失败: {} - 错误信息: {}", server.getHostname(), result.getMessage());
                return false;
            }
        } catch (Exception e) {
            log.error("[rclone安装错误] 在服务器 {} 上安装rclone时出错: {}", server.getHostname(), e.getMessage(), e);
            return false;
        }
    }

    /**
     * 下载rclone安装包到项目目录
     */
    private void downloadRclonePackage() {
        try {
            String projectRoot = System.getProperty("user.dir");
            String scriptDir = projectRoot + "/scripts";
            
            // 检查是否已有安装包
            File linuxPackage = new File(RCLONE_LINUX_PACKAGE);
            File windowsPackage = new File(RCLONE_WINDOWS_PACKAGE);
            
            if (linuxPackage.exists() && windowsPackage.exists()) {
                log.info("[安装包存在] rclone安装包已存在，跳过下载");
                return;
            }
            
            log.info("[开始下载] 开始下载rclone安装包到本地...");
            
            // 创建script目录（如果不存在）
            File dir = new File(scriptDir);
            if (!dir.exists()) {
                dir.mkdirs();
                log.info("[目录创建] 创建scripts目录: {}", scriptDir);
            }
            
            // 下载Linux安装包
            if (!linuxPackage.exists()) {
                String linuxDownloadUrl = "https://downloads.rclone.org/rclone-current-linux-amd64.zip";
                log.info("[Linux下载] 开始下载Linux安装包: {}", linuxDownloadUrl);
                downloadFile(linuxDownloadUrl, linuxPackage.getAbsolutePath());
                log.info("[Linux完成] Linux安装包下载完成: {} (大小: {} 字节)", linuxPackage.getAbsolutePath(), linuxPackage.length());
            }
            
            // 下载Windows安装包
            if (!windowsPackage.exists()) {
                String windowsDownloadUrl = "https://downloads.rclone.org/rclone-current-windows-amd64.zip";
                log.info("[Windows下载] 开始下载Windows安装包: {}", windowsDownloadUrl);
                downloadFile(windowsDownloadUrl, windowsPackage.getAbsolutePath());
                log.info("[Windows完成] Windows安装包下载完成: {} (大小: {} 字节)", windowsPackage.getAbsolutePath(), windowsPackage.length());
            }
        } catch (Exception e) {
            log.error("[下载错误] 下载rclone安装包时出错: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 下载rclone到项目目录
     */
    private void downloadRcloneToProject() {
        try {
            // 首先下载安装包
            downloadRclonePackage();
            
            // 检查本地是否已有安装脚本
            File linuxScript = new File(RCLONE_SCRIPT_LINUX);
            File windowsScript = new File(RCLONE_SCRIPT_WINDOWS);
            
            if (linuxScript.exists() && windowsScript.exists()) {
                log.info("[脚本存在] rclone安装脚本已存在，跳过下载");
                return;
            }
            
            log.info("[脚本下载] 开始下载rclone安装脚本到项目目录");
            
            // 下载Linux安装脚本
            if (!linuxScript.exists()) {
                // 使用项目中已有的详细版脚本
                File sourceLinuxScript = new File(PROJECT_ROOT + "/scripts/install-rclone-detailed.sh");
                if (sourceLinuxScript.exists()) {
                    Files.copy(sourceLinuxScript.toPath(), linuxScript.toPath(), StandardCopyOption.REPLACE_EXISTING);
                    log.info("[Linux脚本复制] Linux安装脚本复制完成: {}", RCLONE_SCRIPT_LINUX);
                } else {
                    log.warn("[Linux脚本缺失] 未找到详细版Linux安装脚本，使用默认方法");
                    String linuxScriptUrl = "https://raw.githubusercontent.com/rclone/rclone/master/docs/content/install.sh";
                    downloadFile(linuxScriptUrl, RCLONE_SCRIPT_LINUX);
                    log.info("[Linux脚本下载] Linux安装脚本下载完成: {}", RCLONE_SCRIPT_LINUX);
                }
            }
            
            // 下载Windows安装脚本
            if (!windowsScript.exists()) {
                // 使用项目中已有的优化脚本
                File sourceWindowsScript = new File(PROJECT_ROOT + "/scripts/install-rclone-optimized.ps1");
                if (sourceWindowsScript.exists()) {
                    Files.copy(sourceWindowsScript.toPath(), windowsScript.toPath(), StandardCopyOption.REPLACE_EXISTING);
                    log.info("[Windows脚本复制] Windows安装脚本复制完成: {}", RCLONE_SCRIPT_WINDOWS);
                } else {
                    log.warn("[Windows脚本缺失] 未找到优化的Windows安装脚本，使用项目中的脚本");
                    // 注意：rclone官方没有提供标准的Windows安装脚本，我们使用项目中已有的脚本
                    log.info("[Windows脚本存在] Windows安装脚本已存在于项目中: {}", RCLONE_SCRIPT_WINDOWS);
                }
            }
        } catch (Exception e) {
            log.error("[脚本下载错误] 下载rclone安装脚本时出错: {}", e.getMessage(), e);
            throw new RuntimeException("下载rclone安装脚本失败: " + e.getMessage());
        }
    }
    
    /**
     * 下载文件
     * @param fileUrl 文件URL
     * @param filePath 保存路径
     * @throws IOException IO异常
     */
    private void downloadFile(String fileUrl, String filePath) throws IOException {
        log.info("[文件下载] 开始下载文件: {} -> {}", fileUrl, filePath);
        try (InputStream in = new URL(fileUrl).openStream()) {
            Path path = Paths.get(filePath);
            Files.createDirectories(path.getParent()); // 确保父目录存在
            Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
            log.info("[下载完成] 文件下载完成: {} (大小: {} 字节)", filePath, Files.size(path));
        }
    }
    
    /**
     * 在Linux系统上安装rclone
     * @param server 服务器
     * @return 安装结果
     */
    private SshService.CommandResult installRcloneOnLinux(Server server) {
        try {
            log.info("[Linux备用安装] 在Linux服务器 {} 上安装rclone (备用方法)", server.getHostname());
            
            // 上传安装脚本
            String uploadScriptCommand = "echo '#!/bin/bash\n" +
                "set -e\n" +
                "echo \"开始增强版rclone安装...\"\n" +
                "# 检查是否以root权限运行\n" +
                "if [[ \\$EUID -eq 0 ]]; then\n" +
                "    echo \"以root用户运行\"\n" +
                "    USE_SUDO=\"\"\n" +
                "else\n" +
                "    echo \"以普通用户运行\"\n" +
                "    if command -v sudo &> /dev/null; then\n" +
                "        USE_SUDO=\"sudo\"\n" +
                "    else\n" +
                "        echo \"警告: 未找到sudo命令\"\n" +
                "        USE_SUDO=\"\"\n" +
                "    fi\n" +
                "fi\n" +
                "# 检查系统架构\n" +
                "ARCH=\\$(uname -m)\n" +
                "echo \"系统架构: \\$ARCH\"\n" +
                "# 根据架构确定正确的下载URL\n" +
                "case \\$ARCH in\n" +
                "    x86_64)\n" +
                "        RCLONE_ARCH=\"amd64\"\n" +
                "        ;;\n" +
                "    aarch64|arm64)\n" +
                "        RCLONE_ARCH=\"arm64\"\n" +
                "        ;;\n" +
                "    armv7l)\n" +
                "        RCLONE_ARCH=\"arm-v7\"\n" +
                "        ;;\n" +
                "    i386|i686)\n" +
                "        RCLONE_ARCH=\"386\"\n" +
                "        ;;\n" +
                "    *)\n" +
                "        echo \"警告: 未知架构 \\$ARCH，使用amd64作为默认值\"\n" +
                "        RCLONE_ARCH=\"amd64\"\n" +
                "        ;;\n" +
                "esac\n" +
                "echo \"使用架构: \\$RCLONE_ARCH\"\n" +
                "# 下载并安装rclone\n" +
                "echo \"下载rclone...\"\n" +
                "DOWNLOAD_URL=\"https://downloads.rclone.org/rclone-current-linux-\\$RCLONE_ARCH.zip\"\n" +
                "# 尝试使用curl下载\n" +
                "if command -v curl &> /dev/null; then\n" +
                "    echo \"使用curl下载...\"\n" +
                "    curl -L -o rclone-current-linux-\\$RCLONE_ARCH.zip \\$DOWNLOAD_URL\n" +
                "elif command -v wget &> /dev/null; then\n" +
                "    echo \"使用wget下载...\"\n" +
                "    wget -O rclone-current-linux-\\$RCLONE_ARCH.zip \\$DOWNLOAD_URL\n" +
                "else\n" +
                "    echo \"错误: 未找到curl或wget命令，请先安装其中一个\"\n" +
                "    exit 1\n" +
                "fi\n" +
                "# 检查下载是否成功\n" +
                "if [ ! -f \"rclone-current-linux-\\$RCLONE_ARCH.zip\" ]; then\n" +
                "    echo \"错误: 下载失败\"\n" +
                "    exit 1\n" +
                "fi\n" +
                "# 解压文件\n" +
                "echo \"解压文件...\"\n" +
                "unzip -q rclone-current-linux-\\$RCLONE_ARCH.zip\n" +
                "# 获取解压后的目录名\n" +
                "RCLONE_DIR=\\$(ls -d rclone-*-linux-\\$RCLONE_ARCH 2>/dev/null | head -n 1)\n" +
                "if [ -z \"\\$RCLONE_DIR\" ]; then\n" +
                "    echo \"错误: 无法找到解压后的rclone目录\"\n" +
                "    exit 1\n" +
                "fi\n" +
                "echo \"找到目录: \\$RCLONE_DIR\"\n" +
                "# 进入目录\n" +
                "cd \\$RCLONE_DIR\n" +
                "# 复制二进制文件到系统路径\n" +
                "echo \"安装rclone...\"\n" +
                "\\$USE_SUDO cp rclone /usr/local/bin/\n" +
                "\\$USE_SUDO chown root:root /usr/local/bin/rclone\n" +
                "\\$USE_SUDO chmod 755 /usr/local/bin/rclone\n" +
                "# 安装man页面（如果存在）\n" +
                "if [ -f \"rclone.1\" ]; then\n" +
                "    \\$USE_SUDO mkdir -p /usr/local/share/man/man1\n" +
                "    \\$USE_SUDO cp rclone.1 /usr/local/share/man/man1/\n" +
                "    \\$USE_SUDO mandb 2>/dev/null || echo \"无法更新man数据库\"\n" +
                "fi\n" +
                "# 创建配置目录\n" +
                "mkdir -p ~/.config/rclone\n" +
                "# 返回上级目录\n" +
                "cd ..\n" +
                "# 清理临时文件\n" +
                "echo \"清理临时文件...\"\n" +
                "rm -rf rclone-current-linux-\\$RCLONE_ARCH.zip \\$RCLONE_DIR\n" +
                "# 验证安装\n" +
                "echo \"验证安装...\"\n" +
                "if command -v rclone &> /dev/null; then\n" +
                "    VERSION_OUTPUT=\\$(rclone --version 2>&1)\n" +
                "    if [ \\$? -eq 0 ]; then\n" +
                "        echo \"rclone安装成功!\"\n" +
                "        echo \"\\$VERSION_OUTPUT\"\n" +
                "        exit 0\n" +
                "    else\n" +
                "        echo \"错误: rclone命令存在但无法执行\"\n" +
                "        exit 1\n" +
                "    fi\n" +
                "else\n" +
                "    echo \"错误: rclone安装失败，命令未找到\"\n" +
                "    exit 1\n" +
                "fi' > /tmp/install-rclone-enhanced.sh && chmod +x /tmp/install-rclone-enhanced.sh";
            
            log.info("[脚本上传] 上传安装脚本到服务器 {}", server.getHostname());
            SshService.CommandResult uploadResult = sshService.executeCommand(server, uploadScriptCommand);
            if (!uploadResult.isSuccess()) {
                log.error("[脚本上传失败] 上传安装脚本失败: {} - 错误信息: {}", server.getHostname(), uploadResult.getMessage());
                return uploadResult;
            }
            
            // 执行安装脚本
            String installCommand = "bash /tmp/install-rclone-enhanced.sh";
            log.info("[执行命令] 执行安装命令: {} - 服务器: {}", installCommand, server.getHostname());
            
            SshService.CommandResult result = sshService.executeCommand(server, installCommand);
            return result;
        } catch (Exception e) {
            log.error("[Linux安装错误] 在Linux服务器 {} 上安装rclone时出错: {}", server.getHostname(), e.getMessage(), e);
            SshService.CommandResult errorResult = new SshService.CommandResult();
            errorResult.setSuccess(false);
            errorResult.setMessage("安装rclone时出错: " + e.getMessage());
            return errorResult;
        }
    }
    
    /**
     * 在Windows系统上安装rclone
     * @param server 服务器
     * @return 安装结果
     */
    private SshService.CommandResult installRcloneOnWindows(Server server) {
        try {
            log.info("[Windows备用安装] 在Windows服务器 {} 上安装rclone (备用方法)", server.getHostname());
            
            // 上传安装脚本
            String uploadScriptCommand = "echo ^# rclone安装脚本^ > C:\\temp\\install-rclone.ps1 && " +
                "echo ^# 用于在Windows系统上安装rclone^ >> C:\\temp\\install-rclone.ps1 && " +
                "echo Write-Host \"开始安装rclone...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo ^$isAdmin = ^(^[Security.Principal.WindowsPrincipal^] ^[Security.Principal.WindowsIdentity^]::GetCurrent^(^)^).IsInRole^(^[Security.Principal.WindowsBuiltInRole^] \"Administrator\"^) >> C:\\temp\\install-rclone.ps1 && " +
                "echo if ^(^-not ^$isAdmin^) { >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"请以管理员权限运行此脚本\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     exit 1 >> C:\\temp\\install-rclone.ps1 && " +
                "echo } >> C:\\temp\\install-rclone.ps1 && " +
                "echo try { >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$arch = ^$env:PROCESSOR_ARCHITECTURE >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"系统架构: ^$arch\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$downloadUrl = \"\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     if ^(^^$arch -eq \"AMD64\"^) { >> C:\\temp\\install-rclone.ps1 && " +
                "echo         ^$downloadUrl = \"https://downloads.rclone.org/rclone-current-windows-amd64.zip\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     } elseif ^(^^$arch -eq \"x86\"^) { >> C:\\temp\\install-rclone.ps1 && " +
                "echo         ^$downloadUrl = \"https://downloads.rclone.org/rclone-current-windows-386.zip\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     } else { >> C:\\temp\\install-rclone.ps1 && " +
                "echo         Write-Host \"不支持的系统架构: ^$arch\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo         exit 1 >> C:\\temp\\install-rclone.ps1 && " +
                "echo     } >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"下载rclone...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$zipFile = \"^$env:TEMP\\rclone.zip\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Invoke-WebRequest -Uri ^$downloadUrl -OutFile ^$zipFile >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"解压文件...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$extractPath = \"^$env:TEMP\\rclone_extract\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Expand-Archive -Path ^$zipFile -DestinationPath ^$extractPath -Force >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$rcloneDir = Get-ChildItem -Path ^$extractPath -Directory ^| Select-Object -First 1 >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"安装rclone...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$installPath = \"C:\\Program Files\\rclone\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     if ^(^!^(Test-Path ^$installPath^)^) { >> C:\\temp\\install-rclone.ps1 && " +
                "echo         New-Item -ItemType Directory -Path ^$installPath -Force >> C:\\temp\\install-rclone.ps1 && " +
                "echo     } >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Copy-Item -Path \"^^(^$rcloneDir.FullName^)\\rclone.exe\" -Destination ^$installPath -Force >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$envPath = ^[Environment^]::GetEnvironmentVariable^(\"Path\", ^[EnvironmentVariableTarget^]::Machine^) >> C:\\temp\\install-rclone.ps1 && " +
                "echo     if ^(^^$envPath -notlike \"*^$installPath*\"^) { >> C:\\temp\\install-rclone.ps1 && " +
                "echo         ^[Environment^]::SetEnvironmentVariable^(\"Path\", \"^$envPath;^$installPath\", ^[EnvironmentVariableTarget^]::Machine^) >> C:\\temp\\install-rclone.ps1 && " +
                "echo         Write-Host \"已将rclone添加到系统PATH\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     } >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"清理临时文件...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Remove-Item -Path ^$zipFile -Force >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Remove-Item -Path ^$extractPath -Recurse -Force >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"验证安装...\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     ^$rcloneVersion = ^& rclone --version >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host ^$rcloneVersion >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"rclone安装完成!\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo } catch { >> C:\\temp\\install-rclone.ps1 && " +
                "echo     Write-Host \"安装过程中出现错误: ^$^(^_.Exception.Message^)\" >> C:\\temp\\install-rclone.ps1 && " +
                "echo     exit 1 >> C:\\temp\\install-rclone.ps1 && " +
                "echo } >> C:\\temp\\install-rclone.ps1";
            
            log.info("[脚本上传] 上传安装脚本到服务器 {}", server.getHostname());
            SshService.CommandResult uploadResult = sshService.executeCommand(server, uploadScriptCommand);
            if (!uploadResult.isSuccess()) {
                log.error("[脚本上传失败] 上传安装脚本失败: {} - 错误信息: {}", server.getHostname(), uploadResult.getMessage());
                return uploadResult;
            }
            
            // 执行安装脚本
            String installCommand = "powershell -ExecutionPolicy Bypass -File C:\\temp\\install-rclone.ps1";
            log.info("[执行命令] 执行安装命令: {} - 服务器: {}", installCommand, server.getHostname());
            
            SshService.CommandResult result = sshService.executeCommand(server, installCommand);
            return result;
        } catch (Exception e) {
            log.error("[Windows安装错误] 在Windows服务器 {} 上安装rclone时出错: {}", server.getHostname(), e.getMessage(), e);
            SshService.CommandResult errorResult = new SshService.CommandResult();
            errorResult.setSuccess(false);
            errorResult.setMessage("安装rclone时出错: " + e.getMessage());
            return errorResult;
        }
    }
}